package mmc.server.shell.ops;

import java.util.ArrayList;
import java.util.List;

import mmc.server.shell.*;

public abstract class Operation {

    public String text;
    protected Operation lValue;
    protected Operation rValue;

    /**
     * This constructor is only allowed to be used by overriding implementations of operation if they perform their own syntax check (e.g. a Function)
     * 
     * @param text name of the Operation
     */
    protected Operation(String text){
        this.text = text;
    }

    protected Operation(String text, String regex, boolean lValueRequired, boolean rValueRequired) throws ShellException {
        this.text = text;
        
        parse(regex, lValueRequired, rValueRequired);
    }
    
    protected void parse(String regex, boolean lValueRequired, boolean rValueRequired) throws ShellException{
        List<Integer> lopsPos = posOfRegex(regex);

        if (lopsPos.size()==0) {
            throw new OperationNotAcceptedException(false, text + " does not contain " + regex);
        }
        ShellException e = null;
        
        if(!isRightAssociative()){ //search from right to left then (-> execute from left to right)
        	List<Integer> rewind = new ArrayList<Integer>(lopsPos.size());
        	
        	for(int i=lopsPos.size()-1;i>=0;i--){
        		rewind.add(lopsPos.get(i));
        	}
        	
        	lopsPos = rewind;
        }
        
        if (Shell.OP_PARSE_TRACE > 0) {
        	for(int i=0;i<Shell.OP_PARSE_TRACE_INDENT;i++){
        		System.out.print("   ");
        	}
        	System.out.println("detected "+getClass().getSimpleName()+" @"+lopsPos);
        }

        for(Integer opsPos : lopsPos){
        	try{
		        if (Shell.OP_PARSE_TRACE > 0) {
		        	Shell.OP_PARSE_TRACE_INDENT++;
		        	for(int i=0;i<Shell.OP_PARSE_TRACE_INDENT;i++){
		        		System.out.print("   ");
		        	}
		        	System.out.println("checking "+getClass().getSimpleName()+" @"+opsPos);
		        }
		        
		        if (lValueRequired) {
		            if (opsPos < 1) {
		                throw new OperationNotAcceptedException(false, text + " not accepted because there is no space for the lValue");
		            }
		
		            lValue = parseLValue(text.substring(0, opsPos).trim());
		        } else {
		            if (opsPos > 0) {
		                throw new OperationNotAcceptedException(false, text + " there is a lValue although the operation does not need one");
		            }
		        }
		
		        if (rValueRequired) {
		            if (opsPos >= text.length() - regex.length()) {
		                throw new OperationNotAcceptedException(false, text + " not accepted because there is no space for the rValue");
		            }
		
		            rValue = parseRValue(text.substring(opsPos + regex.length()).trim());
		        } else {
		            if (opsPos < text.length() - regex.length()) {
		                throw new OperationNotAcceptedException(false, text + " there is a rValue although the operation does not need one");
		            }
		        }
		        
		        return;
        	}catch(ShellException ee){
        		e=ee;
        	}finally{
        		if (Shell.OP_PARSE_TRACE > 0) {
		        	Shell.OP_PARSE_TRACE_INDENT--;
		        }
        	}
        }
        
        throw e;
    }

    protected Operation parseLValue(String text) throws ShellException{
    	return Shell.getOperationTreeFor(text);
    }
    
    protected Operation parseRValue(String text) throws ShellException{
    	return Shell.getOperationTreeFor(text);
    }
    
    protected int firstPosOfRegex(String regex) throws ShellException {
    	List<Integer> ret=posOfRegex(regex);
    	
    	return ret.size()==0?-1:ret.get(0);
    }
    
    protected List<Integer> posOfRegex(String regex) throws ShellException {
    	List<Integer> ret = new ArrayList<Integer>();
        int klammerEbene = 0;
        boolean quoted=false;

        for (int i = 0; i < text.length(); i++) {
        	if (text.charAt(i) == '\\') {
        		i++;
        	}else if (text.charAt(i) == '"') {
        		quoted = !quoted;
        	} else if (!quoted&&text.charAt(i) == '(') {
                klammerEbene++;
            } else if (!quoted&&text.charAt(i) == ')') {
                klammerEbene--;
                if (klammerEbene < 0) {
                    throw new ShellException("Negative Bracket Layer detected (pos=" + i + "):" + text);
                }
            } else if (!quoted && klammerEbene == 0 && text.regionMatches(true, i, regex, 0, regex.length())) {
                ret.add(i);
            }
        }

        return ret;
    }

    abstract public double execute();

    @Override
    public String toString() {
        return this.getClass().toString() + ": " + text;
    }

    public static PriorityType getPriorityType() {
        return PriorityType.CONSTANT;
    }
    
    protected static String formatHelpString(String name, String desc){
    	return String.format("%-15s %s", name, desc);
    }
    
    public boolean hasDoubleOutput(){
    	return Util.isAssignableToDouble(getOutputType());
    }
    
    public Object executeComplex(){
    	return execute();
    }
    
    public Class<?> getOutputType(){
    	return double.class;
    }
    
    public Object executeDefault(){
    	if(hasDoubleOutput()){
    		return execute();
    	}else{
    		return executeComplex();
    	}
    }
    
    public boolean hasOutput(){
    	return !getOutputType().equals(void.class);
    }
    
    public boolean isRightAssociative(){
    	return true;
    }
}
